ostree: Add collection and ref bindings to metadata on commit
authorKrzesimir Nowak <krzesimir@kinvolk.io>
Thu, 22 Jun 2017 19:49:22 +0000 (21:49 +0200)
committerAtomic Bot <atomic-devel@projectatomic.io>
Thu, 6 Jul 2017 19:08:14 +0000 (19:08 +0000)
The collection and ref bindings are stored in the commit metadata
under ostree.collection-binding and ostree.ref-binding,
respectively. They will be used to verify if the commit really comes
from the collection and ref we wanted to pull from.

Closes: #972
Approved by: cgwalters

src/libostree/ostree-repo-private.h
src/ostree/ot-builtin-commit.c
tests/basic-test.sh

index 407e2cb3a6f082356fea688e262e4af4378a028a..dc49ed3be3c579b6f01fcb16532beeab800cd9b3 100644 (file)
@@ -56,6 +56,10 @@ G_BEGIN_DECLS
  * in a summary file. */
 #define OSTREE_COMMIT_TIMESTAMP "ostree.commit.timestamp"
 
+/* Well-known keys for the commit metadata */
+#define OSTREE_REF_BINDING "ostree.ref-binding"
+#define OSTREE_COLLECTION_BINDING "ostree.collection-binding"
+
 typedef enum {
   OSTREE_REPO_TEST_ERROR_PRE_COMMIT = (1 << 0)
 } OstreeRepoTestErrorFlags;
index 8e8b9233ff6b85cee0d5a70339e6d72ac10dd4e3..35340a04bbc660cc2fc8e38e1b8c1a8375343255 100644 (file)
@@ -29,6 +29,7 @@
 #include "otutil.h"
 #include "ot-tool-util.h"
 #include "parse-datetime.h"
+#include "ostree-repo-private.h"
 
 static char *opt_subject;
 static char *opt_body;
@@ -36,6 +37,7 @@ static char *opt_body_file;
 static gboolean opt_editor;
 static char *opt_parent;
 static gboolean opt_orphan;
+static char **opt_bind_refs;
 static char *opt_branch;
 static char *opt_statoverride_file;
 static char *opt_skiplist_file;
@@ -78,6 +80,7 @@ static GOptionEntry options[] = {
   { "editor", 'e', 0, G_OPTION_ARG_NONE, &opt_editor, "Use an editor to write the commit message", NULL },
   { "branch", 'b', 0, G_OPTION_ARG_STRING, &opt_branch, "Branch", "BRANCH" },
   { "orphan", 0, 0, G_OPTION_ARG_NONE, &opt_orphan, "Create a commit without writing a ref", NULL },
+  { "bind-ref", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_bind_refs, "Add a ref to ref binding commit metadata", "BRANCH" },
   { "tree", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_trees, "Overlay the given argument as a tree", "dir=PATH or tar=TARFILE or ref=COMMIT" },
   { "add-metadata-string", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_metadata_strings, "Add a key/value pair to metadata", "KEY=VALUE" },
   { "add-detached-metadata-string", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_detached_metadata_strings, "Add a key/value pair to detached metadata", "KEY=VALUE" },
@@ -303,6 +306,70 @@ parse_keyvalue_strings (char             **strings,
   return TRUE;
 }
 
+#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
+static void
+add_collection_binding (OstreeRepo       *repo,
+                        GVariantBuilder  *metadata_builder)
+{
+  const char *collection_id = ostree_repo_get_collection_id (repo);
+
+  if (collection_id == NULL)
+    return;
+
+  g_variant_builder_add (metadata_builder, "{s@v}", OSTREE_COLLECTION_BINDING,
+                         g_variant_new_variant (g_variant_new_string (collection_id)));
+}
+#endif  /* OSTREE_ENABLE_EXPERIMENTAL_API */
+
+static int
+compare_strings (gconstpointer a, gconstpointer b)
+{
+  const char **sa = (const char **)a;
+  const char **sb = (const char **)b;
+
+  return strcmp (*sa, *sb);
+}
+
+static void
+add_ref_binding (GVariantBuilder *metadata_builder)
+{
+  if (opt_orphan)
+    return;
+
+  g_assert_nonnull (opt_branch);
+
+  g_autoptr(GPtrArray) refs = g_ptr_array_new ();
+  g_ptr_array_add (refs, opt_branch);
+  for (char **iter = opt_bind_refs; iter != NULL && *iter != NULL; ++iter)
+    g_ptr_array_add (refs, *iter);
+  g_ptr_array_sort (refs, compare_strings);
+  g_autoptr(GVariant) refs_v = g_variant_new_strv ((const char *const *)refs->pdata,
+                                                   refs->len);
+  g_variant_builder_add (metadata_builder, "{s@v}", OSTREE_REF_BINDING,
+                         g_variant_new_variant (g_steal_pointer (&refs_v)));
+}
+
+static void
+fill_bindings (OstreeRepo    *repo,
+               GVariant      *metadata,
+               GVariant     **out_metadata)
+{
+  g_autoptr(GVariantBuilder) metadata_builder =
+    ot_util_variant_builder_from_variant (metadata, G_VARIANT_TYPE_VARDICT);
+
+  add_ref_binding (metadata_builder);
+
+#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
+  /* Allow the collection ID to be overridden using
+   * --add-metadata-string=ostree.collection-binding=blah */
+  if (metadata == NULL ||
+      !g_variant_lookup (metadata, OSTREE_COLLECTION_BINDING, "*", NULL))
+    add_collection_binding (repo, metadata_builder);
+#endif  /* OSTREE_ENABLE_EXPERIMENTAL_API */
+
+  *out_metadata = g_variant_ref_sink (g_variant_builder_end (metadata_builder));
+}
+
 gboolean
 ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError **error)
 {
@@ -559,6 +626,10 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
     {
       gboolean update_summary;
       guint64 timestamp;
+      g_autoptr(GVariant) old_metadata = g_steal_pointer (&metadata);
+
+      fill_bindings (repo, old_metadata, &metadata);
+
       if (!opt_timestamp)
         {
           GDateTime *now = g_date_time_new_now_utc ();
index 07bbeddf9d058aeebf661428339140a4558d5a99..33fdde9d6ce21eafdfe58aae84d3d9d722a4fc3c 100644 (file)
@@ -171,8 +171,10 @@ assert_streq $($OSTREE log test2-no-parent |grep '^commit' | wc -l) "1"
 echo "ok commit no parent"
 
 cd ${test_tmpdir}
-empty_rev=$($OSTREE commit ${COMMIT_ARGS} -b test2-no-subject -s '' --timestamp="2005-10-29 12:43:29 +0000" $test_tmpdir/checkout-test2-4)
-omitted_rev=$($OSTREE commit ${COMMIT_ARGS} -b test2-no-subject-2 --timestamp="2005-10-29 12:43:29 +0000" $test_tmpdir/checkout-test2-4)
+# Do the --bind-ref=<the other test branch>, so we store both branches sorted
+# in metadata and thus the checksums remain the same.
+empty_rev=$($OSTREE commit ${COMMIT_ARGS} -b test2-no-subject --bind-ref=test2-no-subject-2 -s '' --timestamp="2005-10-29 12:43:29 +0000" $test_tmpdir/checkout-test2-4)
+omitted_rev=$($OSTREE commit ${COMMIT_ARGS} -b test2-no-subject-2 --bind-ref=test2-no-subject --timestamp="2005-10-29 12:43:29 +0000" $test_tmpdir/checkout-test2-4)
 assert_streq $empty_rev $omitted_rev
 echo "ok commit no subject"